package compiler.tac;

import java.util.ArrayList;

public class Instruction {
	public enum Operator {
		Add, Sub, Mul, Div, Cmp, Neg,
		Bra, Beq, Bne, Blt, Ble, Bgt, Bge,
		Ld, Str, Mv, Rd, Wrt, End, Phi, BigPhi
		, Prm, Call, Rtn
	}
	Operator oprt; 
	Address dest; // null if oprt is one of Store, Write, End, and all branches.
	Address oprdl; // null if oprt is one of Read, End, Phi.
	Address oprdr; // null if oprt is one of Neg, Load, Move, Bra, Read, Write, End, Phi.
	String lable;
	ArrayList<Address> phiArg = null; // not null only if oprt is Phi.
	ArrayList<Boolean> hasrealuse = null;
	//SimpleExpt destExpt;
	//ArrayList<String> bigPhiArg;
	String exptVersion = null;
	Boolean reload=null;
	ArrayList<Instruction> def = null;
	ArrayList<Boolean> insert = null;
	Boolean save = null;
	ArrayList<Boolean> processed=null;
	public Instruction (Operator oprt, Address dest, Address oprdl, Address oprdr, String lab) {
		this.oprt = oprt;
		this.dest = dest;
		this.oprdl = oprdl;
		this.oprdr = oprdr;
		this.lable = lab;
		//this.phiArg = null;
		//hasrealuse = null;
		//this.destExpt = null;
		//this.bigPhiArg = null;
	}
	
	public Instruction (Operator oprt, Address dest, Address oprdl, Address oprdr) {
		this.oprt = oprt;
		this.dest = dest;
		this.oprdl = oprdl;
		this.oprdr = oprdr;
		this.lable = null;
		//this.phiArg = null;
		//this.destExpt = null;
		//this.bigPhiArg = null;
	}
	// construct Phi instruction
	public Instruction (Address dest, int size) {
		this.oprt = Operator.Phi;
		this.dest = dest;
		this.oprdl = null;
		this.oprdr = null;
		this.phiArg = new ArrayList<Address>();
		for(int i = 0; i < size; i ++)
			phiArg.add(new Address(""));
		//this.destExpt = null;
		//this.bigPhiArg = null;
	}
	
	// construct BigPhi instruction
	public Instruction (SimpleExpt d, int size) {
		this.oprt = Operator.BigPhi;
		this.dest = new Address(d.toString());
		this.oprdl = null;
		this.oprdr = null;
		this.phiArg = new ArrayList<Address>(size);
		hasrealuse = new ArrayList<Boolean>();
		def=new ArrayList<Instruction>();
		insert = new ArrayList<Boolean>(); 
		processed = new ArrayList<Boolean>();
		for(int i = 0; i < size; i ++)
		{
			phiArg.add(new Address(d.toString()));
			hasrealuse.add(null);
			def.add(null);
			insert.add(false);
			processed.add(false);
		}
		
	}
	
	
	public boolean isLater()
	{
		if(phiArg!=null)
			for(int i = 0; i < phiArg.size(); i ++)
				if(SimpleExpt.getVersion(phiArg.get(i).getId())!=-1 && hasrealuse.get(i))
					return true;
		return false;
	}
	
	public boolean isOccurence(SimpleExpt expt)
	{
		if(oprt == Operator.BigPhi && SimpleExpt.cutName(dest.getId()).equals(expt.toString()))
			return true;
		if(exptVersion!=null && SimpleExpt.cutName(exptVersion).equals(expt.toString()))
			return true;
		return false;
	}
	public boolean containPhiArgVersion(int version)
	{
		if(phiArg !=null)
			for(Address add: phiArg)
				if(SimpleExpt.getVersion(add.getId())==version)
					return true;
		return false;
	}
	public int versionPosition(int version)
	{
		if(phiArg !=null)
			for(int i = 0; i < phiArg.size(); i ++)
				if(SimpleExpt.getVersion(phiArg.get(i).getId())==version)
					return i;
		return -1;
	}
	public String toString() {
		String result = "\t"+oprt.name()+"\t";
		if(dest != null && dest.getId() != null)
			result = result + " " + dest.getId();
		if(oprdl != null && oprdl.getId() != null)
			result = result + " " + oprdl.getId();
		if(oprdl != null && oprdl.getAddress() != null)
			result = result + " " + oprdl.getAddress().getLable();
		if(oprdr != null && oprdr.getId() != null)
			result = result + " " + oprdr.getId();
		if(oprdr != null && oprdr.getAddress() != null)
			result = result + " " + oprdr.getAddress().getLable();
		if(phiArg != null)
		{	
			for(Address arg: phiArg)
				result = result + " " + arg.getId();
		}
		if(exptVersion!=null)
			result+= " "+ exptVersion;
		return result;
	}
	
	public void replacePhiArg(int idx, String var) {
		phiArg.set(idx, new Address(var));
	}
	
	public boolean isUsing(String id) {
		switch (oprt) {
		case Add:
		case Sub:
		case Mul:
		case Div:
		case Cmp:
			return id.equals(oprdl.getId()) || id.equals(oprdr.getId());
		case Neg:
		case Ld:
		case Mv:
		case Wrt:
			return id.equals(oprdl.getId());
		case Str:
			return id.equals(oprdr.getId());
		default:
			return false;
		}
	}
	
	public boolean hasDestination(String id) {
		switch (oprt) {
		case Add:
		case Sub:
		case Mul:
		case Div:
		case Cmp:
		case Neg:
		case Ld:
		case Mv:
		case Rd:
		case Phi:
			return id.equals(dest.getId());
		default:
			return false;
		}
	}
}
